#include "bfifo.h"

#define MAX_BFIFO_SIZE 16384

#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif

/**
 * @brief  返回一定长度下的最小分配数，2^n
 */
uint16_t bfifo_size_align(uint16_t size)
{
    uint16_t minSize = 1;

    if (0 == size) return 0;
    while (minSize < size) minSize <<= 1;
    return minSize;
}

/**
 * @brief  初始化一块FIFO，指定缓冲区
 */
void bfifo_init(pbfifo_t f, uint8_t *buff, uint16_t size)
{
    size = bfifo_size_align(size);
    memset(f, 0, sizeof(bfifo_t));
    f->buf  = buff;
    f->mask = size - 1;
    f->len  = size;
}

/**
 * @brief  往缓冲区写入1字节数据
 */
void bfifo_in_byte(pbfifo_t f, uint8_t b)
{
    if (bfifo_avail(f) > 0) {
        uint16_t off = f->ptr_in & f->mask;
        f->buf[off]  = b;
        f->ptr_in++;
    }
}

/**
 * @brief  从缓冲区读出1字节数据
 */
bool bfifo_out_byte(pbfifo_t f, uint8_t *b)
{    
    if (bfifo_len(f) == 0) {
        return false;  // 缓冲区为空
    }

    uint16_t off = f->ptr_out & f->mask;
    *b = f->buf[off];
    f->ptr_out++;  // 注意：多线程/中断环境下可能需要原子操作或加锁
    return true;
}

/**
 * @brief  往缓冲区写入数据
 */
int bfifo_in(pbfifo_t f, const void *src, uint16_t len)
{
    uint16_t avail = bfifo_avail(f);
    uint16_t offset, l;
    uint16_t buf_len = f->len;
    
    if (avail < len) {
        len = avail;
    }
    if (len == 0) {
        return 0;
    }

    offset = f->ptr_in & f->mask;
    l      = MIN(len, buf_len - offset);

    memcpy(f->buf + offset, src, l);
    if (l < len) {
        memcpy(f->buf, (uint8_t *)src + l, len - l);
    }
    f->ptr_in += len;
    return len;
}

/**
 * @brief  从缓冲区读出数据
 */
int bfifo_out(pbfifo_t f, void *dst, uint16_t len)
{
    uint16_t used = bfifo_len(f);
    uint16_t offset, l;
    uint16_t buf_len = f->len;
    if (used < len) {
        len = used;
    }

    offset = f->ptr_out & f->mask;
    l      = MIN(len, buf_len - offset);

    memcpy(dst, f->buf + offset, l);
    memcpy((uint8_t *)dst + l, f->buf, len - l);
    f->ptr_out += len;
    return len;
}

/**
 * @brief  复位缓冲区
 */
void bfifo_reset(pbfifo_t f)
{
    if (f) {
        f->ptr_in  = 0;
        f->ptr_out = 0;
    }
}
